/*
 * CNetworkSpeaker.cpp
 *
 *  Created on: 11.08.2011
 *      Author: kirill
 *      Copyright: LGPL
 */

#include "CNetworkSpeaker.h"
#include <boost/thread/thread.hpp>
#include <stdlib.h>

CNetworkSpeaker::CNetworkSpeaker():
	incomeQueue(NULL), ConnectingStart(false), endpoint(NULL), acceptor(NULL)
{

}

CNetworkSpeaker::CNetworkSpeaker( TThreadSafeQueue<STANDART_NETWORK_MESSAGE*>* IncomeQueue ):
	incomeQueue(IncomeQueue), ConnectingStart(false), endpoint(NULL), acceptor(NULL)
{

}

CNetworkSpeaker::~CNetworkSpeaker()
{
	if ( endpoint != NULL )
	{
		delete endpoint;
	}
	if ( acceptor != NULL )
	{
		delete acceptor;
	}
}

bool
CNetworkSpeaker::ConnectionStatusCallback( const UINT ClientId, const UINT64 Error )
{
	bool Result = false;
	switch ( Error )
	{
	break;case ERROR_NETWORK_NONE:  //There are no mistakes, connection is created
		{
			fprintf( stderr, "[SPEACKER] ConnectionStatusCallback() - Connected - %u \n", ClientId );
			BYTE ConnectionResult = connectionCreateCallback( currentConnection, ClientId );
			if ( ConnectionResult & CREATE_CONNECTION_RESULT_OK )
			{
				Result = true; //Connection accepted by NetworkManager
				ClientFreeSlots[ClientId] = false;
			}
			if ( ConnectionResult & CREATE_CONNECTION_RESULT_NEXT )
			{
				UINT CurrentConnectionID = GetFreeSlot();

				fprintf( stderr, "[SPEACKER] ConnectionStatusCallback() - Create new connection - %u \n", CurrentConnectionID );
				currentConnection = new CServerConnection( CurrentConnectionID,
		                                                   boost::bind( &CNetworkSpeaker::ConnectionStatusCallback, this, _1, _2 ),
		                                                   acceptor, endpoint, &io_service, incomeQueue );
				currentConnection->Start();
			}
			else
			{
				ConnectingStart = false; // Setup flag for GoOnConnecting()
			}
		}
	break;default:
		{
			fprintf( stderr, "[SPEACKER] ConnectionStatusCallback() - disconnect - %u \n", ClientId );
			ClientFreeSlots[ClientId] = true;// Clear slot if connection failed
		}
	}
	return Result;
}

void
CNetworkSpeaker::StartConnection( const boost::asio::ip::address_v4 *IpAddress, const UINT FirstPort, const UINT LastPort,
								  boost::function <BYTE( IConnection*,const UINT )> ConnectionCreateCallback )
{
	connectionCreateCallback = ConnectionCreateCallback;

	ConnectingStart = true;
	memset( ClientFreeSlots, true, sizeof(ClientFreeSlots) );

	UINT workPort = FindWorkPort( FirstPort, LastPort );
    UINT CurrentConnectionID = GetFreeSlot();

	if ( NULL == IpAddress ) //Server
	{
		fprintf( stderr, "[SPEACKER] Start server connection \n"  );
		if ( endpoint != NULL )
		{
			delete endpoint;
		}
		if ( acceptor != NULL )
		{
			delete acceptor;
		}

		endpoint = new boost::asio::ip::tcp::endpoint ( boost::asio::ip::tcp::v4(), workPort );
		acceptor = new boost::asio::ip::tcp::acceptor( io_service, *endpoint );

		acceptor->listen();

		currentConnection = new CServerConnection( CurrentConnectionID,
                                                   boost::bind( &CNetworkSpeaker::ConnectionStatusCallback, this, _1, _2 ),
                                                   acceptor, endpoint, &io_service, incomeQueue );
	}
	else //Client
	{
		fprintf( stderr, "[SPEACKER] Start client connection \n" );
		if ( endpoint != NULL )
		{
			delete endpoint;
		}

		endpoint = new boost::asio::ip::tcp::endpoint ( *IpAddress, workPort );

		currentConnection = new CClientConnection( CurrentConnectionID,
                                                   boost::bind( &CNetworkSpeaker::ConnectionStatusCallback, this, _1, _2 ),
                                                   endpoint, &io_service, incomeQueue );
	}

	currentConnection->Start();
	IoservRun = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
}

UINT
CNetworkSpeaker::FindWorkPort( const UINT FirstPort, const UINT LastPort )
{
	if ( ( FirstPort == LastPort ) || ( LastPort < FirstPort ) )
	{
		return FirstPort;
	}
	return FirstPort;
	// TODO Подбор рабочего порта по средствам перебора и подъёма сервера.
}

UINT
CNetworkSpeaker::GetFreeSlot()
{
	for ( UINT i = 0; i < MAX_PLAYERS_COUNT; i++ )
	{
		if ( ClientFreeSlots[i] )
		{
			return i;
		}
	}
	fprintf( stderr, "GetFreeSlot failed!" );
	return MAX_PLAYERS_COUNT; // TODO Эпик фэйл
}

bool
CNetworkSpeaker::GoOnConnecting()
{
	bool Result = false;

	fprintf( stderr, "[GoOnConnecting] ConnectingStart: %u\n", ConnectingStart );
	if ( ConnectingStart == false )// TODO Отвратительное дублирование кодаS
	{
		ConnectingStart = true;

		UINT CurrentConnectionID = GetFreeSlot();
		currentConnection = new CServerConnection( CurrentConnectionID,
                                                   boost::bind( &CNetworkSpeaker::ConnectionStatusCallback, this, _1, _2 ),
                                                   acceptor, endpoint, &io_service, incomeQueue );
		currentConnection->Start();
		IoservRun = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
		Result = true;
	}
	return Result;
}

void
CNetworkSpeaker::PauseConnecting()
{
	ConnectingStart = false;
}
